﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using lumo.display.object2D;
using lumo.display.animation2D;
using Microsoft.Xna.Framework;
using lumo.input;

namespace lumo.display.gui
{
    /// <summary>
    /// Base command list class.
    /// </summary>
    public abstract class CommandListBase : ICommandList
    {
        /// <summary></summary>
        internal ParallelAnimation2D ParallelAnimation = new ParallelAnimation2D();
        internal Dictionary<object, ParallelAnimation2D> ChangeAnimationDictionary   = new Dictionary<object, ParallelAnimation2D>();
        internal List<object>        CommandList = new List<object>();

        // Private variables.
        private bool Setupped  = false;
        private bool Finalized = false;

        // Menu Input Variables
        /// <summary>Cursor Object</summary>
        public Cursor       Cursor = null;
        /// <summary>Increase Index Input Object</summary>
        public IInputObject IncreaseIndexInput = null;
        /// <summary>Decrease Index Input Object</summary>
        public IInputObject DecreaseIndexInput = null;

        /// <summary>
        /// Add an item.
        /// </summary>
        public void Add(object Item)
        {
            CheckItemAdded(Item);
            CommandList.Add(Item);
        }

        /// <summary>
        /// Initialize the command List.
        /// </summary>
        public void Initialize(float Time)
        {
            ChangeAnimationDictionary.Clear();
            foreach (object obj in CommandList)
                ChangeAnimationDictionary[obj] = new ParallelAnimation2D(obj);
            if (_Active && (_Index < 0))
                UpdateSelection(-1, true);
            OnInitialize(Time);
            Setupped = false;
        }

        /// <summary>
        /// Finalize the command List.
        /// </summary>
        public void Terminate(float Time)
        {
            OnTerminate(Time);
            Finalized = true;
        }

        #region Abstract Methods

        /// <summary>
        /// Check the item added.
        /// </summary>
        protected abstract void CheckItemAdded(object Item);

        /// <summary>
        /// Get boundaries of an item
        /// </summary>
        protected abstract Rectangle? GetBoundaries(int Index);

        /// <summary>
        /// Initialize the item.
        /// </summary>
        protected abstract void OnInitialize(float Time);

        /// <summary>
        /// Setup the item after initialize.
        /// </summary>
        protected abstract void OnSetup();

        /// <summary>
        /// Terminate the menu.
        /// </summary>
        protected abstract void OnTerminate(float Time);

        /// <summary>
        /// Unselect the item.
        /// </summary>
        protected abstract void OnUnselectItem(int ItemIndex, object Item);

        /// <summary>
        /// Select the item.
        /// </summary>
        protected abstract void OnSelectItem(int ItemIndex, object Item);

        /// <summary>
        /// Update.
        /// </summary>
        protected abstract void OnDraw(object Item, Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch);

        /// <summary>
        /// Update.
        /// </summary>
        protected virtual void OnChangeIndex(int LastIndex, int Index) {}

        /// <summary>
        /// Update.
        /// </summary>
        protected virtual void OnUpdate() { }

        #endregion

        #region Interface Implementation

        #region ICommandList

        /// <summary>Private index value.</summary>
        private int _Index = -1;

        /// <summary>
        /// Index of menu
        /// </summary>
        public int Index
        {
            get 
            {
                return _Index;
            }
            set
            {
                int LastIndex = _Index;
                if ((CommandList.Count <= 0) || (value < 0))
                    _Index = -1;
                else
                    _Index = value % CommandList.Count;
                if (Setupped)
                    ChangeIndex(LastIndex);
            }
        }

        /// <summary>
        /// Count of items.
        /// </summary>
        public int Count { get { return CommandList.Count; } }

        #endregion

        #region IObejct2D

        /// <summary>
        /// Draw implementaion of IObject2D
        /// </summary>
        /// <param name="spriteBatch"></param>
        public void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
        {
            if (_Visible)
            {
                foreach(object obj in CommandList)
                    OnDraw(obj, spriteBatch);
            }
        }

        /// <summary>Private draw order float.</summary>
        private float _DrawOrder = 0f;
        /// <summary>
        /// Simple implementation of draw order.
        /// </summary>
        public float DrawOrder { get { return _DrawOrder; } set { _DrawOrder = value; } }

        /// <summary>Private visible variable.</summary>
        private bool _Visible = true;        /// <summary>
        /// Simple implementation of Visible.
        /// </summary>
        public bool Visible { get { return _Visible; } set { _Visible = value; } }

        #endregion

        #region IPositionable2D

        /// <summary> 
        /// Coordinates where the object is drawn 
        /// </summary>
        private Vector2 _Position = Vector2.Zero;
        /// <summary>
        /// Implementation of Position. IPositionable2D.
        /// </summary>
        public Vector2 Position { get { return _Position; } set { _Position = value; } }
        /// <summary>
        /// Implementation of X. IPositionable2D.
        /// </summary>
        public float X { get { return _Position.X; } set { _Position.X = value; } }
        /// <summary>
        /// Implementation of Y. IPositionable2D.
        /// </summary>
        public float Y { get { return _Position.Y; } set { _Position.Y = value; } }

        #endregion

        #region IUpdatable

        /// <summary>
        /// Frame id for object.
        /// </summary>
        public uint FrameID { get; private set; }

        /// <summary>
        /// Update the menu.
        /// </summary>
        public void Update(LumoComponent Game)
        {
            this.FrameID = Game.FrameID;
            OnUpdate();
            ParallelAnimation.Update(Game);
            foreach (var obj in ChangeAnimationDictionary)
                obj.Value.Update(Game);
            if (Cursor != null)
                Cursor.Update(Game);
            if (IncreaseIndexInput != null)
                IncreaseIndexInput.Update(Game);
            if (DecreaseIndexInput != null)
                DecreaseIndexInput.Update(Game);
            if (Finalized)
                return;
            if (!Setupped)
            {
                if (ParallelAnimation.Finished)
                {
                    OnSetup();

                    if (_Active && (_Index < 0))
                    {
                        int LastIndex = _Index;
                        UpdateSelection(LastIndex, true);
                        ChangeIndex(LastIndex);
                    }
                    Setupped = true;
                }
            }
            else
            {
                if (_Active)
                {
                    int LastIndex = _Index;
                    UpdateSelection(LastIndex);
                    ChangeIndex(LastIndex);
                }
            }
        }

        /// <summary>
        /// Change the index
        /// </summary>
        private bool ChangeIndex(int LastIndex)
        {
            if (LastIndex != _Index)
            {
                OnChangeIndex(LastIndex, _Index);
                if (LastIndex >= 0)
                    OnUnselectItem(LastIndex, CommandList[LastIndex]);
                if (_Index >= 0)
                    OnSelectItem(_Index, CommandList[_Index]);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Update item selection
        /// </summary>
        protected void UpdateSelection(int LastIndex, bool Force = false)
        {
            if (CommandList.Count <= 0)
                return;
            if (Cursor != null && (Force || Cursor.Moving))
            {
                _Index = -1;
                for (int i = 0; i < CommandList.Count; ++i)
                {
                    Rectangle? Boundaries = GetBoundaries(i);
                    if (!Boundaries.HasValue)
                        continue;
                    if ((Cursor.X < Boundaries.Value.Left) || (Cursor.X > Boundaries.Value.Right) || (Cursor.Y < Boundaries.Value.Top) || (Cursor.Y > Boundaries.Value.Bottom))
                        continue;
                    _Index = i;
                    break;
                }
                if (_Index != LastIndex)
                    return;
            }
            if ((IncreaseIndexInput != null) && (IncreaseIndexInput.Triggered))
            {
                if (_Index < 0)
                    _Index = 0;
                else
                    _Index = (_Index + 1) % CommandList.Count;
            }
            else if ((DecreaseIndexInput != null) && (DecreaseIndexInput.Triggered))
            {
                if (_Index < 0)
                    _Index = (CommandList.Count - 1);
                else
                    _Index = (_Index + (CommandList.Count - 1)) % CommandList.Count;
            }
        }

        #endregion

        #region IActivatable

        /// <summary>Private active variable</summary>
        private bool _Active = true;
        /// <summary>
        /// Active property
        /// </summary>
        public bool Active { get { return _Active; } set { _Active = value; } }

        #endregion

        #endregion
    }
}
